package org.vy.dal;

import java.io.IOException;
import java.io.InputStream;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
import java.util.Vector;

public class ConnectionManager {
	
	private boolean bPooling;

	private Vector<DBConnection> available;

	private Vector<DBConnection> busy;

	private String driver;

	private String url;

	private String user;

	private String password;

	private static Properties prop;

	private int maxSize;

	private int initSize;

	private static ConnectionManager s_inst = null;
	
	static {
		
		prop = new Properties();
		InputStream fis = null;
		try{
	        fis = ConnectionManager.class.getResourceAsStream("db.properties"); //new FileInputStream("../../../db.properties");
	        prop.load(fis);
		}catch(Exception e){
			e.printStackTrace();
			if(fis != null){
				try{
					fis.close();
				}catch(Exception ex){
					//DO Nothing
					ex.printStackTrace();
				}
			}
		}
	}
	
	private ConnectionManager() throws Exception
	{
		this.bPooling = Boolean.parseBoolean(prop.getProperty("pooling.enable"));
		this.url = prop.getProperty("db.url");
		this.user = prop.getProperty("db.user");
		this.password = prop.getProperty("db.password");
		this.driver = prop.getProperty("db.driver");
		this.maxSize = Integer.parseInt(prop.getProperty("db.pool.maxsize"));
		this.initSize = Integer.parseInt(prop.getProperty("db.pool.initsize"));
//		this.connectionTimeout = Integer.parseInt(prop.getProperty("db.pool.timeout"));
		available = new Vector<DBConnection>();
		busy = new Vector<DBConnection>();
		
		init();
	}
	
	public static ConnectionManager getInstance() throws Exception{
		if(s_inst == null){
			if(prop == null){
				throw new IOException("Unable to initialize the Connection Manager");
			}
			s_inst = new ConnectionManager();
		}
		return s_inst;
	}

	protected void init() throws ClassNotFoundException, SQLException
	{
		if(bPooling)
		{
			for(int i=0; i < initSize; i++)
			{
				available.add(createNewConnection());
			}
		}
	}

	private DBConnection createNewConnection() throws ClassNotFoundException, SQLException
	{
		Class.forName(driver);
		DBConnection dbConn = new DBConnection(DriverManager.getConnection(url, user, password));
		return dbConn;
	}


	public DBConnection getConnection() throws SQLException, ClassNotFoundException
	{
		DBConnection dbConn;

		if(!bPooling)	// no pooling case
		{
			dbConn = createNewConnection();
		}
		else		// get connection from pool
		{
			for(int i=0; i < available.size(); i++)
			{
				dbConn = (DBConnection)available.get(i);

				if(dbConn.isClosed())
				{
					available.remove(i);
					i--;
				}
				else
				{
					busy.add(available.remove(i));
					break;
				}
			}

			if(maxSize > (available.size() + busy.size()))
			{
				dbConn = createNewConnection();
				busy.add(dbConn);
			}
			else
			{
				throw new SQLException("Maximum database connection limit reached");
			}
		}

		return dbConn;
	}


	public void freeConnection(DBConnection dbConn) throws Exception
	{
		if(bPooling)
		{
			busy.remove(dbConn);
			if(!dbConn.isClosed())
			{
				dbConn.setAutoCommit(true);
				dbConn.closeStatements();
				available.add(dbConn);
			}
		}
		else
		{
			dbConn.close();
		}
	}

	public void finalize()
	{
		DBConnection dbConn;
		for(int i=0; available.size() > 0 ; )
		{
			try
			{
				dbConn = (DBConnection)available.remove(i);
				dbConn.close();
			}
			catch(Exception ex)
			{
				ex.printStackTrace();
			}
		}

		for(int i=0; busy.size() > 0 ; )
		{
			try
			{
				dbConn = (DBConnection)busy.remove(i);
				dbConn.close();
			}
			catch(Exception ex)
			{
				ex.printStackTrace();
			}
		}
	}
}
